<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  <link rel="shortcut icon" type="image/ico" href="favicon.ico" />
  <title>SlickGrid example: Optimizing Updates</title>
  <link rel="stylesheet" href="../slick.grid.css" type="text/css"/>
  <link rel="stylesheet" href="../css/smoothness/jquery-ui.css" type="text/css"/>
  <link rel="stylesheet" href="../controls/slick.pager.css" type="text/css"/>
  <link rel="stylesheet" href="examples.css" type="text/css"/>
  <style>
    .cell-title {
      font-weight: bold;
    }

    .cell-effort-driven {
      text-align: center;
    }

    .cell-selection {
      border-right-color: silver;
      border-right-style: solid;
      background: #f5f5f5;
      color: gray;
      text-align: right;
      font-size: 10px;
    }
  </style>
</head>
<body>
<div style="position:relative">
  <div style="width:600px;">
    <div class="grid-header" style="width:100%">
      <label>SlickGrid</label>
    </div>
    <div id="myGrid" style="width:100%;height:500px;"></div>
    <div id="pager" style="width:100%;height:20px;"></div>
  </div>

  <div class="options-panel">
    <b>Description:</b>
    <hr/>
   
    <p>
      This page demonstrates how the bulk update operations can be used efficiently
      delete and insert new rows into the table. The example simulates a log viewer
      where for each log severity always 100 items are shown in a ring-buffer fashion.
      If you use the browser profiler to compare the two different operations below 
      you will notice the significant performance difference which can be crucial for 
      systems where a lot of new events are provided from an external source and 
      then updated.
    </p>
    <h2>Controls</h2>
    <button onclick="simulateLogs(100000, false, true)">Add 100000 entries of each severity (inefficient)</button>
    <button onclick="simulateLogs(100000, true, true)">Add 100000 entries of each severity (efficient)</button>
    <div>
        Last update took: <span id="duration"></span>
    </div>
    <h2>View Source:</h2>
    <ul>
      <li><a href="https://github.com/6pac/SlickGrid/blob/master/examples/example-optimizing-updates.html" target="_sourcewindow"> View the source for this example on Github</a></li>
    </ul>
  </div>
</div>

<script src="../lib/jquery-1.12.4.min.js"></script>
<script src="../lib/jquery-ui.min.js"></script>
<script src="../lib/jquery.event.drag-2.3.0.js"></script>

<script src="../slick.core.js"></script>
<script src="../slick.formatters.js"></script>
<script src="../slick.grid.js"></script>
<script src="../slick.dataview.js"></script>
<script src="../controls/slick.pager.js"></script>

<script>
  var dataView;
  var grid;
  var data = [];
  var columns = [
    {id: "sel", name: "#", field: "num", behavior: "select", cssClass: "cell-selection", width: 40, resizable: false, selectable: false },
    {id: "severity", name: "Severity", field: "severity", width: 120, minWidth: 120, cssClass: "cell-severity"},
    {id: "time", name: "Time", field: "time", width: 120, minWidth: 120, cssClass: "cell-time"},
    {id: "message", name: "Message", field: "message", minWidth: 300, cssClass: "cell-message"},
  ];

  var options = {
    editable: false,
    enableAddRow: false,
    enableCellNavigation: true
  };

  var eventIndex = 0;
  var severities = ['Info', 'Warning', 'Error'];
  var maxItemsPerSeverity = 100;
  
  function LogEntry() {
    eventIndex++;
    this.id = "id_" + eventIndex;
    this.severity = severities[eventIndex % severities.length];
    this.time = new Date().toISOString().substring(0,19);
    this.message = "Log Entry " + eventIndex;
  }
    
  // our ringbuffer to keep only 10 items per severity
  var logsPerSeverity = {}; 
  for (var i = 0; i < severities.length; i++) {
    logsPerSeverity[severities[i]] = [];
  }  
  
  // the logic to simulate new events
  
  function simulateLogs(count, efficient, update) {
    var chunks = 10;
    var start = new Date();
    for(var i = 0; i < chunks; i++) {
        simulateLogChunk(count / chunks, efficient);
    }
    var end = new Date();
    if (update) {
        $('#duration').text(end - start + 'ms');
    }      
  }
  function simulateLogChunk(count, efficient, update) {
    if(efficient) {
      dataView.beginUpdate(true); // efficient bulk update 
      var logs = []; // we will first collect all items to be added
      for (var i = 0; i < count; i++) {
        var log = new LogEntry();
        var group = logsPerSeverity[log.severity];
        if(group.length > maxItemsPerSeverity) {
          dataView.deleteItem(group.pop().id);
        }
        
        group.unshift(log);
        logs[i] = log;
      }
      // then we insert all items in one go.
      dataView.insertItems(0, logs);    
      dataView.endUpdate();          
    } else {
      dataView.beginUpdate(); // just a normal update 
      for (var i = 0; i < count; i++) {
        var log = new LogEntry();
        var group = logsPerSeverity[log.severity];
        if(group.length > maxItemsPerSeverity) {
          try {
            dataView.deleteItem(group.pop().id);
          } catch(e) {
            // item not yet fully inserted
          }
        }
        
        group.unshift(log);
        dataView.insertItem(0, log); // individual inserts
      }
      dataView.endUpdate();  
    }
  }

  $(function () {
    dataView = new Slick.Data.DataView({ inlineFilters: true });
    grid = new Slick.Grid("#myGrid", dataView, columns, options);
    var pager = new Slick.Controls.Pager(dataView, grid, $("#pager"));

    // wire up model events to drive the grid
    dataView.onRowCountChanged.subscribe(function (e, args) {
      grid.updateRowCount();
      grid.render();
    });

    dataView.onRowsChanged.subscribe(function (e, args) {
      grid.invalidateRows(args.rows);
      grid.render();
    });

    // prepare some data
    simulateLogs(severities.length * maxItemsPerSeverity, true)
  })
</script>
</body>
</html>
